<template>
  <div id="app">
    <h1>AES ENCRYPTION</h1>
    <div class="println m-b-60 m-l-200">
      <span>
        <div>USERNAME:</div>
        <input v-model="user.username"
               autofocus
               placeholder="Username" />
      </span>
      <span>
        <div>PASSWORD:</div>
        <input v-model="user.password"
               placeholder="Password" />
      </span>
    </div>
    <div class="println"
         v-for="(item,key,i) in resultMap"
         :key="i">
      <span class="println-name">{{key}}:</span>
      <span class="println-value">{{item}}</span>
    </div>
    <div class="error m-l-200"
         v-if="message">{{message}}</div>
  </div>
</template>

<script>
// "crypto-js": "^3.1.9-1"
import CryptoJS from 'crypto-js'
export default {
  name: 'App',
  data() {
    return {
      // 调试打印 - 调试用
      resultMap: {},
      message: '',
      // 盐值用到的参数
      user: {
        username: 'test',
        password: '123456',
      },
    }
  },
  created() {
    this.init()
  },
  watch: {
    ['user.username']() {
      if (this.user.password !== '') {
        this.init()
      } else {
        this.resultMap = {}
        this.message = 'RAW_TEXT invalid'
      }
    },
    ['user.password'](raw) {
      if (raw !== '') {
        this.init()
      } else {
        this.resultMap = {}
        this.message = 'RAW_TEXT invalid'
      }
    },
  },
  methods: {
    init() {
      // 使用用户名合成盐值
      const salt = CryptoJS.MD5(this.user.username).toString()
      // 基数
      const base = (v) => (v.length - 16) / 2
      // 从盐中取出密钥
      const key = salt.slice(base(salt), base(salt) + 16)
      // 从盐中取出向量
      let iv = salt.slice(0, base(salt)) + salt.slice(base(salt) + 16)
      // 向量偏移量
      iv = iv.slice(base(iv), base(iv) + 16)
      // Base64化后的加密密文
      let ciphertext = this.encrypt(this.user.password, key, iv)

      // 调试打印 - 调试用
      this.println('RAW_TEXT', this.user.password)
      this.println('SALT', salt)
      this.println('KEY', key)
      this.println('IV', iv)
      this.println('RAW_CIPHERTEXT', CryptoJS.enc.Base64.parse(ciphertext))
      this.println('CIPHERTEXT', ciphertext)
    },
    /**
     * 原文加密
     * @param src 原文值
     * @param key 密钥值
     * @param iv 向量值
     */
    encrypt(src, key, iv) {
      const encrypted = CryptoJS.AES.encrypt(
        CryptoJS.enc.Utf8.parse(src),
        CryptoJS.enc.Utf8.parse(key),
        {
          iv: CryptoJS.enc.Utf8.parse(iv),
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.Pkcs7,
        }
      )
      return CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
    },
    // 结果打印
    println(name, value) {
      this.resultMap[name] = value
    },
  },
}
</script>

<style>
html,
body {
  width: 100%;
  height: 100%;
  font-size: 18px;
  line-height: 1.5;
  margin: 0 auto;
  color: azure;
  background-color: #1e1e1e;
  font-family: Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
span {
  display: inline-block;
  vertical-align: middle;
}
input {
  width: 280px;
  height: 44px;
  outline: none;
  padding: 0 5px;
  border: 0;
  background-color: #404040;
  color: azure;
  font-size: 16px;
}
input:hover,
input:focus {
  background-color: #2c2c2c;
}
#app {
  padding: 60px 0;
}
#app h1 {
  padding-bottom: 60px;
  padding-left: 200px;
}
.println-name {
  display: inline-block;
  width: 200px;
  text-align: right;
  padding-right: 5px;
  color: #569cd6;
}
.println-value {
  color: #ce9178;
}
.m-b-60 {
  margin-bottom: 60px;
}
.m-l-200 {
  margin-left: 200px;
}
.error {
  color: red;
}
</style>
